package com.xfdmao.fcat.coin.base.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xfdmao.fcat.coin.base.constant.KLineConstant;
import com.xfdmao.fcat.coin.base.entity.KlineInfo;
import com.xfdmao.fcat.coin.base.entity.Matrix;
import com.xfdmao.fcat.coin.entity.Strategy;
import com.qwrt.base.common.util.DateUtil;
import com.qwrt.base.common.util.StrUtil;

import java.math.BigDecimal;
import java.util.*;

/**
 * Created by cissa on 2019/7/28.
 */
public class StrategyUtil {

    /**
     * 各合约对应的最新的建仓
     * key:用户名+symbol+contractType
     * value:建仓K线对应的时间
     */
    private static Map<String, String> openMap = new HashMap<>();

    /**
     * 做空BTC的矩阵
     *
     * @param klineInfos
     */
    public static List<Matrix> sellBTCMatrix(List<KlineInfo> klineInfos, int maxMA, Strategy strategy) {
        System.out.println("找出最佳的两条做空均线值");
        double[][] mns = new double[maxMA][maxMA];
        boolean[][] top20 = new boolean[maxMA][maxMA];
        String kineInfosJson = JSONArray.toJSONString(klineInfos); //由于在每次循环计算过程中，当发生
        for (int m = 0; m < maxMA; m++) {
            for (int n = 0; n < maxMA; n++) {
                if (m >= n) {
                    List<KlineInfo> klineInfoList = JSONArray.parseArray(kineInfosJson, KlineInfo.class);
                    List<KlineInfo> result = sellBTC(klineInfoList, m + 1, n + 1, strategy);
                    KlineInfoUtil.dealFee(result);
                    double income = KlineInfoUtil.getSumIncomeRateRealList(result);
                    if (income > 0) {
                        mns[m][n] = income;
                    } else {
                        mns[m][n] = 0.0;
                    }
                } else {
                    mns[m][n] = 0.0;
                }

            }
        }
        List<Matrix> matrixList = MatrixUtil.dealMatrix(mns);
        MatrixUtil.print(mns);


        Matrix matrix = MatrixUtil.queryMaxMatrix(mns);
        Integer maxM = matrix.getM();
        Integer maxN = matrix.getN();
        System.out.println("最佳的策略：" + JSONObject.toJSONString(matrix));
        return matrixList;
    }

    /**
     * 做多BTC的矩阵
     *
     * @param klineInfos
     */
    public static List<Matrix> buyBTCMatrix(List<KlineInfo> klineInfos, int maxMA, Strategy strategy) {
        System.out.println("找出最佳的两条做多均线值");
        double[][] mns = new double[maxMA][maxMA];
        boolean[][] top20 = new boolean[maxMA][maxMA];
        String kineInfosJson = JSONArray.toJSONString(klineInfos); //由于在每次循环计算过程中，当发生
        for (int m = 0; m < maxMA; m++) {
            for (int n = 0; n < maxMA; n++) {
                if (m >= n) {
                    List<KlineInfo> klineInfoList = JSONArray.parseArray(kineInfosJson, KlineInfo.class);
                    List<KlineInfo> result = buyBTC(klineInfoList, m + 1, n + 1, strategy);
                    KlineInfoUtil.dealFee(result);
                    double income = KlineInfoUtil.getSumIncomeRateRealList(result);
                    if (income > 0) {
                        mns[m][n] = income;
                    } else {
                        mns[m][n] = 0.0;
                    }
                } else {
                    mns[m][n] = 0.0;
                }

            }
        }
        List<Matrix> matrixList = MatrixUtil.dealMatrix(mns);
        MatrixUtil.print(mns);


        Matrix matrix = MatrixUtil.queryMaxMatrix(mns);
        Integer maxM = matrix.getM();
        Integer maxN = matrix.getN();
        System.out.println("最佳的策略：" + JSONObject.toJSONString(matrix));
        return matrixList;
    }

    /**
     * 做多BTC
     */
    public static List<KlineInfo> buyBTC(List<KlineInfo> klineInfos, Integer upMaNum, Integer downMaNum, Strategy strategy) {
        Map<Date, Double> upAvgMap = KlineInfoUtil.getAvg(klineInfos, upMaNum);
        Map<Date, Double> downAvgMap = KlineInfoUtil.getAvg(klineInfos, downMaNum);
        List<KlineInfo> result = dealBuyBTC(klineInfos, upAvgMap, downAvgMap, strategy);
        return result;
    }

    /**
     * 处理做多策略
     *
     * @param klineInfos
     * @param upAvgMap
     * @param downAvgMap
     * @return
     */
    private static List<KlineInfo> dealBuyBTC(List<KlineInfo> klineInfos, Map<Date, Double> upAvgMap, Map<Date, Double> downAvgMap, Strategy strategy) {
        double upperLeadGain = 0.007;
        Double downRate = null;
        Double towKlineGain = null;
        Double upperLeadGainB = null;
        Double gain = null;
        Double gainB = null;
        Double overGain = null;
        Integer klineCount = null;
        if (!StrUtil.isBlank(strategy.getParam())) {
            try {
                JSONObject param = JSONObject.parseObject(strategy.getParam());
                downRate = param.getDouble("downRate");
                towKlineGain = param.getDouble("towKlineGain");
                upperLeadGain = param.getDouble("upperLeadGain");
                upperLeadGainB = param.getDouble("upperLeadGainB");
                gain = param.getDouble("gain");
                gainB = param.getDouble("gainB");
                overGain = param.getDouble("overGain");
                klineCount = param.getInteger("klineCount");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        KlineInfoUtil.emptyIncomeRateAndBuySellStatus(klineInfos);
        List<KlineInfo> result1 = new ArrayList<>();
        boolean positionStatus = false;
        String openKey = strategy.getSymbol() + strategy.getContractType() + "buy";
        for (int i = 0; i < klineInfos.size(); i++) {
            if (i < 2) continue;
            KlineInfo klineInfo = klineInfos.get(i);
            if ("0.0".equals(upAvgMap.get(klineInfo.getDate()).toString())) continue;
            //debug到具体某一根K线
            if (klineInfo.getDate().getTime() == DateUtil.toDate("2019-10-07 08:00:00", DateUtil.TIME_PATTERN_DISPLAY).getTime()) {
                System.out.println(JSONObject.toJSONString(klineInfo));
            }
            if (!positionStatus) {
                //买入条件：

                //当前k线收盘价必须大于今日的开盘价
                /* if(!KlineInfoUtil.overDailyOpen(klineInfos,i,klineInfo))continue;*/

                //当最近100根K线内出现跌幅超过0.023，则不操作
                if (overGain(klineInfos, i, overGain, klineCount)) continue;

                //1、当前收盘K线的长期均线值 小于 短期均线的值 (短期均线站上长期均线)
                if (upAvgMap.get(klineInfo.getDate()) > downAvgMap.get(klineInfo.getDate())) continue;

                //2、当前收盘K线的长期均线的值  大于  上一根收盘K线的长期均线的值（即：长期均线往上走）
                if (upAvgMap.get(klineInfo.getDate()) < upAvgMap.get(klineInfos.get(i - 1).getDate())) continue;

                //3、当前收盘K线的收盘价高于短期均线的收盘价 (收盘价站上短期均线)
                if (klineInfo.getClose() < downAvgMap.get(klineInfo.getDate())) continue;

                //4、当前收盘K线不能收长的上引线
                if (KlineInfoUtil.isUpperLead(klineInfo, Double.valueOf(strategy.getUpperLeadFactor()))) continue;

                //5、当前收盘K线的上一个K线不能收上引线
                if (KlineInfoUtil.isUpperLead(klineInfos.get(i - 1), Double.valueOf(strategy.getUpperLeadFactor())))
                    continue;

                //6、当前收盘K线涨幅大于gainFactor
                if (klineInfo.getGain() < Double.valueOf(strategy.getGainFactor())) {
                    if (!(klineInfo.getGain() > gainB &&
                            klineInfos.get(i - 1).getGain() > 0 && klineInfos.get(i - 2).getGain() > 0 &&
                            klineInfos.get(i - 1).getGain() + klineInfos.get(i - 2).getGain() > 0.0015)) {
                        continue;
                    }
                }

                //7、最近5根收盘K线不能出现大于-0.0078个点的跌幅
                if (klineInfos.get(i).getGain() < downRate ||
                        klineInfos.get(i - 1).getGain() < downRate ||
                        klineInfos.get(i - 2).getGain() < downRate ||
                        klineInfos.get(i - 3).getGain() < downRate ||
                        klineInfos.get(i - 4).getGain() < downRate
                ) {
                    continue;
                }

                //8、前两根K线不能全部下跌并且下跌的总和不能大于0.0082
                if (klineInfos.get(i - 1).getGain() < 0 && klineInfos.get(i - 2).getGain() < 0 &&
                        (klineInfos.get(i - 1).getGain() + klineInfos.get(i - 2).getGain()) < towKlineGain) {
                    continue;
                }

                //9、K线涨幅大于0.014则不建仓
                if (klineInfo.getGain() > gain) {
                    continue;
                }

                buyOpen(result1, klineInfo);
                openMap.put(openKey, klineInfo.getDate().getTime() + "");
                positionStatus = !positionStatus;
            } else {
                //卖出条件：

                //当跌幅超过0.023，则平多
                if (overGain(klineInfos, i, overGain, klineCount)) {
                    sellClose(result1, klineInfo);
                    positionStatus = !positionStatus;
                    continue;
                }


                //**保护伞，当价格跌破开仓多少止损**
                double startPrice = result1.get(result1.size() - 1).getClose();//建仓价格
                double protectionPrice = new BigDecimal(startPrice).subtract(new BigDecimal(startPrice).multiply(new BigDecimal(strategy.getProtectionDownFactor()))).doubleValue();
                if (klineInfo.getLow() < protectionPrice) {
                    klineInfo.setClose(protectionPrice);
                    sellClose(result1, klineInfo);
                    positionStatus = !positionStatus;
                } else {
                    //1、当前收盘K线的收盘价 小于 当前收盘K线的短期均线的值
                    //如果买入后的第一根k线收跌则卖出 (klineInfo.getGain()<nextGain  && ((Long.valueOf(openMap.get(openKey)) +15*60*1000)>=klineInfo.getDate().getTime()))
                    //2、如果收盘K线收上引线，并且涨幅为负
                    //3、如果收盘K线收上引线，并且涨幅大于1.1个点
                    if (klineInfo.getClose() < downAvgMap.get(klineInfo.getDate()) ||
                            (klineInfo.getUpLeadGain() >= upperLeadGain && klineInfo.getGain() <= 0) ||
                            klineInfo.getUpLeadGain() >= upperLeadGainB) {
                        sellClose(result1, klineInfo);
                        positionStatus = !positionStatus;
                    }

                }
            }
        }
        return result1;
    }

    /**
     * 做空BTC
     *
     * @param klineInfos
     * @param upMa
     * @param downMa
     * @param strategy
     * @return 成交的K线列表
     */
    public static List<KlineInfo> sellBTC(List<KlineInfo> klineInfos, int upMa, int downMa, Strategy strategy) {
        Map<Date, Double> upAvgMap = KlineInfoUtil.getAvg(klineInfos, upMa);
        Map<Date, Double> downAvgMap = KlineInfoUtil.getAvg(klineInfos, downMa);
        List<KlineInfo> result = dealSellBTC(klineInfos, upAvgMap, downAvgMap, strategy);
        return result;
    }

    /**
     * 处理BTC做空
     *
     * @param klineInfos
     * @param upAvgMap
     * @param downAvgMap
     * @param strategy
     * @return
     */
    private static List<KlineInfo> dealSellBTC(List<KlineInfo> klineInfos, Map<Date, Double> upAvgMap, Map<Date, Double> downAvgMap, Strategy strategy) {
        KlineInfoUtil.emptyIncomeRateAndBuySellStatus(klineInfos);
        List<KlineInfo> result1 = new ArrayList<>();
        boolean positionStatus = false;
        Double overGain = null;
        Integer threeRaiseNum = 20;
        Integer klineCount = 0;
        //下引线
        double downLead = 0.05;
        Integer downLeadKlineCount = 20;
        if (!StrUtil.isBlank(strategy.getParam())) {
            try {
                JSONObject param = JSONObject.parseObject(strategy.getParam());
                overGain = param.getDouble("overGain");
                klineCount = param.getInteger("klineCount");
                threeRaiseNum = param.getInteger("threeRaiseNum");
                downLead = param.getDouble("downLead");
                downLeadKlineCount = param.getInteger("downLeadKlineCount");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        for (int i = 0; i < klineInfos.size(); i++) {
            if (i < 2) continue;
            KlineInfo klineInfo = klineInfos.get(i);

            //debug到具体某一根K线
            if (klineInfo.getDate().getTime() == DateUtil.toDate("2019-07-11 13:45:00", DateUtil.TIME_PATTERN_DISPLAY).getTime()) {
                System.out.println(JSONObject.toJSONString(klineInfo));
            }
            if ("0.0".equals(upAvgMap.get(klineInfo.getDate()).toString())) continue;
            if (!positionStatus) {  //如果空仓,则看是否需要建仓
                //买入条件：

                //收盘价格低于每天开盘价
                /*if(klineInfo.getClose()>klineInfo.getDayOpen())continue;*/

                //如果最近200根k线出现涨幅大于3.2个点，则不建仓
                if (overGain(klineInfos, i, overGain, klineCount)) continue;

                //1、当前收盘K线的长期均线值 大于 短期均线的值      即：长期均线在短期均线的上面
                if (upAvgMap.get(klineInfo.getDate()) < downAvgMap.get(klineInfo.getDate())) continue;

                //2、当前收盘K线的长期均线的值  小于  上一根收盘K线的长期均线的值       即：长期均线往下走
                if (upAvgMap.get(klineInfo.getDate()) > upAvgMap.get(klineInfos.get(i - 1).getDate())) continue;

                //3、当前收盘K线的收盘价低于短期均线的收盘价     即价格跌破短期均线
                if (klineInfo.getClose() > downAvgMap.get(klineInfo.getDate())) continue;

                //4、短期均线翻下
                if (downAvgMap.get(klineInfo.getDate()) > downAvgMap.get(klineInfos.get(i - 1).getDate()))
                    continue;

                //5、当前收盘K线不能收长的下引线
                if (KlineInfoUtil.isDownLead(klineInfo, Double.valueOf(strategy.getUpperLeadFactor())))
                    continue;

                //6、当前收盘K线的上一个K线不能收下引线
                if (KlineInfoUtil.isDownLead(klineInfos.get(i - 1), Double.valueOf(strategy.getUpperLeadFactor())))
                    continue;

                //7、最近20根收盘K线涨幅小于1个点
                if (KlineInfoUtil.overGainFactory(klineInfos, i, threeRaiseNum, Double.valueOf(strategy.getGainFactor())))
                    continue;

/*                //8.最近20根收盘K线的下引线涨幅小于0.5个点
                if (KlineInfoUtil.overDownLead(klineInfos, i, downLeadKlineCount, downLead))
                    continue;*/

                sellOpen(result1, klineInfo);
                positionStatus = !positionStatus;
            } else {
                //卖出条件：

                //如果最近200根k线出现涨幅大于3.2个点,则清仓
                if (overGain(klineInfos, i, overGain, klineCount)) {
                    buyClose(result1, klineInfo);
                    klineInfo.setOptType(KLineConstant.OPTTYPE_BIGGAIN);
                    positionStatus = !positionStatus;
                    continue;
                }
/*                if (KlineInfoUtil.overDownLead(klineInfos, i, downLeadKlineCount, downLead)){
                    buyClose(result1, klineInfo);
                    klineInfo.setOptType(KLineConstant.OPTTYPE_OVERDOWNLEADGAIN);
                    positionStatus = !positionStatus;
                    continue;
                }*/
                //**保护价格，当价格跌破开仓多少止损**
                double startPrice = result1.get(result1.size() - 1).getClose();//建仓价格
                double protectionPrice = new BigDecimal(startPrice).add(new BigDecimal(startPrice).multiply(new BigDecimal(strategy.getProtectionDownFactor()))).doubleValue();
                if (klineInfo.getHigh() > protectionPrice) {
                    klineInfo.setClose(protectionPrice);
                    buyClose(result1, klineInfo);
                    positionStatus = !positionStatus;
                    klineInfo.setOptType(KLineConstant.OPTTYPE_PROTECTION);
                } else {
                    // 1、当前收盘K线的收盘价 高于 当前收盘K线的短期均线的值
                    if (klineInfo.getClose() < downAvgMap.get(klineInfo.getDate())) continue;
                    buyClose(result1, klineInfo);
                    positionStatus = !positionStatus;
                    klineInfo.setOptType(KLineConstant.OPTTYPE_OVERMA);
                }
            }
        }
        return result1;
    }


    /**
     * 当超过一定的涨幅则不考虑开空
     *
     * @param klineInfos
     * @param i
     * @return
     */
    private static boolean overGain(List<KlineInfo> klineInfos, int i, double overGain, Integer klineCount) {
        for (int m = 0; m < klineCount; m++) {
            if (i - m < 0) break;
            if (overGain > 0) {
                if (klineInfos.get(i - m).getGain() >= overGain) {
                    return true;
                }
            } else {
                if (klineInfos.get(i - m).getGain() <= overGain) {
                    return true;
                }
            }

        }
        return false;
    }

    /**
     * 卖出平仓
     *
     * @param result1
     * @param klineInfo
     * @return
     */
    private static void sellClose(List<KlineInfo> result1, KlineInfo klineInfo) {
        klineInfo.setBuySellStatus(KLineConstant.BUYSELLSTATUS_SELL_CLOSE);
        klineInfo.setIncomeRate(new BigDecimal(klineInfo.getClose()).subtract(new BigDecimal(result1.get(result1.size() - 1).getClose())).divide(new BigDecimal(result1.get(result1.size() - 1).getClose()), 6, BigDecimal.ROUND_DOWN).doubleValue());
        result1.add(klineInfo);
    }

    /**
     * 买入开仓
     *
     * @param result1
     * @param klineInfo
     * @return
     */
    private static void buyOpen(List<KlineInfo> result1, KlineInfo klineInfo) {
        klineInfo.setBuySellStatus(KLineConstant.BUYSELLSTATUS_BUY_OPEN);
        result1.add(klineInfo);
    }

    /**
     * 卖出开仓
     *
     * @param result1
     * @param klineInfo
     * @return
     */
    private static void sellOpen(List<KlineInfo> result1, KlineInfo klineInfo) {
        klineInfo.setBuySellStatus(KLineConstant.BUYSELLSTATUS_SELL_OPEN);
        result1.add(klineInfo);
    }

    /**
     * 买入平仓
     *
     * @param result1
     * @param klineInfo
     * @return
     */
    private static void buyClose(List<KlineInfo> result1, KlineInfo klineInfo) {
        klineInfo.setBuySellStatus(KLineConstant.BUYSELLSTATUS_BUY_CLOSE);
        KlineInfo klineInfoPrevious = result1.get(result1.size() - 1);
        klineInfo.setIncomeRate(new BigDecimal(klineInfoPrevious.getClose()).subtract(new BigDecimal(klineInfo.getClose())).divide(new BigDecimal(klineInfoPrevious.getClose()), 6, BigDecimal.ROUND_DOWN).doubleValue());
        result1.add(klineInfo);
    }


    /**
     * 做空BTC
     * @param klineInfos
     * @param strategy
     * @return
     */
    public static List<KlineInfo> sellBTC(List<KlineInfo> klineInfos, Strategy strategy) {
        return StrategyUtil.sellBTC(klineInfos,Integer.valueOf(strategy.getUpMa()),Integer.valueOf(strategy.getDownMa()),strategy);
    }
}
